
using System;
using System.Net;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections.Generic;

namespace MxitAPI {
	
	/**
	 * This class uses an HTTP connection to send messages and recieve a response.
	 * The response is stored in the message object  
	 */
	public class TcpMessageTransceiver : MessageTransceiver {
		
		private static log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );
		
		private TcpClient tcpClient;
		private NetworkStream clientStream;
		
		public TcpMessageTransceiver() {
			
			SetupTcpConnection();
			
			Thread listnerThread = new Thread( new ThreadStart( ListenForResponseProtocolMessages ) );
			listnerThread.Start();
			
		}
		
		public void SetupTcpConnection() {
			
			// Get the server information
			TcpServerInfo serverInfo = Connection.Instance.PrimaryServer as TcpServerInfo;
			if ( serverInfo == null ) 
				throw new MxitException( "An instance of an TcpServerInfo was not found." );
			
			
			tcpClient = new TcpClient();
			
			IPEndPoint serverEndPoint = new IPEndPoint( IPAddress.Parse( serverInfo.Address ), serverInfo.Port );
			tcpClient.Connect( serverEndPoint );
			
			clientStream = tcpClient.GetStream();
			
		}
		
		public override void SendMessage( ProtocolMessage protocolMessage ) {
			
			string protocolMessageString = CreateProtocolMessageString( protocolMessage );
			List<string> responseStrings = SendProtocolMessageString( protocolMessageString );
			ProcessProtocolMessageResponseString( responseStrings, protocolMessage, protocolMessage.MessageResponseHandler );

			
		}
		
		public string CreateProtocolMessageString( ProtocolMessage protocolMessage ) {
			StringBuilder packedString = new StringBuilder();
			
			// Create the ProtocolMessage header
			string[] requestData = protocolMessage.GetRequestData();
			
			log.Debug( requestData );
			
			packedString.Append( "id=" + Connection.Instance.LoginName + "\u0000" );
			packedString.Append( "cm=" + ( int ) protocolMessage.ProtocolMessageType + "\u0000" );
			
			// Create the ProtocolMessage body
            if ( requestData.Length > 0 ) {
                packedString.Append( "ms=" );
                foreach ( string request in requestData ) {
                    packedString.Append( request + "\u0001" );
                }
            }

            // Remove the final field terminator
            packedString.Remove( packedString.Length - 1, 1 );
			
			// Add the length of the message to the beginning
			string protocolMessageString = "ln=" + packedString.Length + "\u0000" + packedString.ToString();
			
			//log.Debug( "string sent to server: " + protocolMessageString );
			
			return protocolMessageString;
		}
		
		public List<string> SendProtocolMessageString( string protocolMessageString ) {
			

			//NetworkStream clientStream = tcpClient.GetStream();
			
			ASCIIEncoding encoder = new ASCIIEncoding();
			byte[] sendBuffer = encoder.GetBytes( protocolMessageString );//"ln=272\u0000id=27844443551\u0000cm=1\u0000ms=ODQL7IQyO2Uag0WV3Ec60g==\u0001E-5.8.2-L-Nokia/N70\u00011\u0001w=240;h=291;dmem=1585;lmem=1;c=4096;a=256;ctyp=1217;fmem=948859;capd=4194336;utf8=false;cc=ZA;cid=0;imei=;la=0;enc=UTF8;ploc=en-US;mcc=0;mnc=0;lac=0\u000148971339-8262-4F09-869B-899D9174D924\u0001522239\u000127\u0001en\u0001cr=");

			List<string> responseStrings;
				
			lock ( this ) {
				clientStream.Write( sendBuffer, 0, sendBuffer.Length );
				clientStream.Flush();
				responseStrings = GetResponseFromServer();	
			}
			

			
			log.Debug( "Response string: " + responseStrings );
			
			//tcpClient.Close();
			//clientStreamReader.Close();
			
			
			
					
			//Thread.Sleep(10000);
			
			return responseStrings;
			
			//Thread.Sleep(10000);
			
		}
		
		/**
		 * Runs in a tight loop checking for messages from the mxit servers and handling them.
		 * Is run in a seperate thread
		 */
		private void ListenForResponseProtocolMessages() {
			
			log.Debug( "Listening for messages from server" );
			while ( true ) {
				lock ( this ) {
					log.Debug( "Got lock" );
					if ( clientStream.DataAvailable ) {
						List<string> messageStringFromServer = GetResponseFromServer();
						log.DebugFormat( "Got from server: [{0}]", messageStringFromServer );
						ProcessProtocolMessageResponseString( messageStringFromServer );
					}
		
				}
				Thread.Sleep(1000);
					
				
			}
		}
		
		private List<string> GetResponseFromServer() {
			
			//StreamReader clientStreamReader = new StreamReader( clientStream );
			// Todo change this
			Stream clientStreamReader = clientStream;
			
			List<string> responseStrings = new List<string>();
			
			int numProtocolMessages = -1;
			
			do {
				
				++numProtocolMessages;
				log.DebugFormat( " Data for protocol Message [{0}] being extracted from the server response", numProtocolMessages );
				
				bool lengthFound = false;
				StringBuilder lengthStringBuilder = new StringBuilder();
				// Read in the first part of the response message that shows how many bytes the rest of the message is
                int readTimeout = clientStream.ReadTimeout;
                if ( readTimeout == 0 )
                    readTimeout = 20000;
                try {
                    clientStream.ReadTimeout = 5000;
                    while ( !lengthFound ) {
                        byte currentByte = ( byte ) clientStreamReader.ReadByte();
                        if ( currentByte != 0 )
                            lengthStringBuilder.Append( Char.ConvertFromUtf32( currentByte ) );
                        else
                            lengthFound = true;
                    }
                }
                catch ( IOException e ) {
                    return null;
                }
                finally {
                    clientStream.ReadTimeout = readTimeout;
                }
				
				log.Debug( "Return packet length part: " + lengthStringBuilder.ToString() );
				
				// Check that the length information exists
				string[] lengthStringArr = lengthStringBuilder.ToString().Split( '=' );
				if ( lengthStringArr.Length == 2 && lengthStringArr[0] != "ln" ) 
					throw new MxitTransceiverException( "The message length could not be read in the tcp response from the mxit servers" );
				
				// Get the length of the response
				int responseLength = Int32.Parse( lengthStringArr[1] );

                //System.Threading.Thread.Sleep( 1000 );
				byte[] receiveBuffer = new byte[ responseLength ];
                /*for ( int i = receiveBuffer.Length - 10; i < receiveBuffer.Length; ++i ) {
                    Console.WriteLine( " | " + receiveBuffer[i] + " | " );
                }
                Console.WriteLine( " END" );*/
                //Console.WriteLine(clientStreamReader.Length);
                int bytesRead = 0;
                while ( bytesRead < responseLength ) {
                    if ( clientStream.DataAvailable )
                        bytesRead += clientStream.Read( receiveBuffer, bytesRead, responseLength - bytesRead );
                    else
                        System.Threading.Thread.Sleep( 50 );
                }
               /* for ( int i = receiveBuffer.Length - 10; i < receiveBuffer.Length; ++i ) {
                    Console.WriteLine( " | " + receiveBuffer[i] + " | " );
                }
				*/
				// Todo use built in method
				responseStrings.Add( Encoding.ASCII.GetString( receiveBuffer ) ); //ConvertByteArrayToString( receiveBuffer ) );
				
	//Int32 bytes = stream.Read(data, 0, data.Length);
    //responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
				
				log.DebugFormat( "Data for this message [{0}]", Encoding.ASCII.GetString( receiveBuffer ).Replace( "\u0000", "!!" ).Replace( "\u000c", "+++" ) );
				
			
			} while ( clientStream.DataAvailable );
			
			return responseStrings;
			
		}
		
		
	}
}
